package interval

import (
	"container/heap"
	"container/list"
	"context"
	"errors"
	"fmt"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
)

/* ========================= 小根堆 ========================= */

type minHeap []*intvListItem

func (h minHeap) Len() int           { return len(h) }
func (h minHeap) Less(i, j int) bool { return h[i].nextTime < h[j].nextTime }

// func (h minHeap) Swap(i, j int)       { h[i], h[j] = h[j], h[i] }
func (h minHeap) Swap(i, j int) {
	h[i], h[j] = h[j], h[i]
	h[i].heapIdx, h[j].heapIdx = i, j
}

func (h *minHeap) Push(x interface{}) {
	*h = append(*h, x.(*intvListItem))
	x.(*intvListItem).heapIdx = len(*h) - 1
}

func (h *minHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[:n-1]
	x.heapIdx = -1
	return x
}

var (
	itemDNA  atomic.Int32
	zeroTime time.Time
)

const maxSleep = 100 * time.Millisecond

const (
	FLAG_WithBeginInterval byte = 1 << iota
)

/* ===================== 任务节点 & 引擎 ====================== */

type intvListItem struct {
	id        interface{}
	interval  time.Duration
	nextTime  int64 // nanosecond
	beginTime time.Time
	dna       int32
	fn        func()
	heapIdx   int
	flag      byte
	wg        sync.WaitGroup
}

// 5W任务触发任务间隔正常
// 并发开启关闭同一个组任务正常
// 不会触发已经关闭的任务
// 传入id GC正常
type IntervalListEngine struct {
	mu         sync.Mutex
	heap       minHeap
	lst        *list.List
	firstDelay time.Duration
	index      map[interface{}]*intvListItem
	timer      *time.Timer
	ctx        context.Context
	cancel     context.CancelFunc
	cond       *sync.Cond // 条件变量，用于堆空时的高效等待
	loopStep   string
}

/* -------------------- 构造函数 -------------------- */
func NewIntervalListEngine() *IntervalListEngine {
	e := &IntervalListEngine{
		lst:        list.New(),
		index:      make(map[interface{}]*intvListItem),
		firstDelay: time.Second,
	}
	e.cond = sync.NewCond(&e.mu) // 初始化条件变量，绑定到现有互斥锁
	e.ctx, e.cancel = context.WithCancel(context.Background())
	heap.Init(&e.heap)
	go e.loop()
	return e
}

func (e *IntervalListEngine) OpenIntervalWithNextRunAt(id interface{}, nextRunAt time.Time, interval time.Duration, fn func()) error {
	if interval <= 0 || fn == nil {
		return errors.New("invalid interval or fn")
	}

	t0 := time.Now()
	e.mu.Lock()
	ms := time.Since(t0)
	if ms > time.Millisecond*100 {
		SlowDebugf("slow lock %v", ms)
	}
	defer e.mu.Unlock()

	now := time.Now().UnixNano()
	if old, ok := e.index[id]; ok { // 更新
		changed := false
		var nextTime int64

		if !nextRunAt.IsZero() {
			nextTime = nextRunAt.UnixNano()
			old.flag |= FLAG_WithBeginInterval // 设置标志位
		} else {
			old.flag &= ^FLAG_WithBeginInterval // 清除标志位
		}

		if old.interval != interval {
			old.interval = interval
			if nextTime == 0 {
				nextTime = now + int64(interval)
			}
		}

		if nextTime != 0 && nextTime != old.nextTime {
			old.nextTime = nextTime
			old.beginTime = time.Unix(0, nextTime)
			changed = true
		}

		if !changed {
			v1 := reflect.ValueOf(fn)
			v2 := reflect.ValueOf(old.fn)
			changed = v1.Pointer() != v2.Pointer()
		}
		old.fn = fn
		if changed {
			old.dna = itemDNA.Add(1)
			if old.heapIdx != -1 { // 有可能未在heap中
				heap.Fix(&e.heap, old.heapIdx)
			}
			DebugStepf("%v update flag:%d dna %d", id, old.flag, old.dna)
		}
	} else { // 新增
		nextTime := now + int64(e.firstDelay)
		it := &intvListItem{
			id:       id,
			interval: interval,
			nextTime: nextTime,
			fn:       fn,
			heapIdx:  -1,
			dna:      itemDNA.Add(1),
		}
		if !nextRunAt.IsZero() {
			it.nextTime = nextRunAt.UnixNano()
			it.beginTime = time.Unix(0, it.nextTime)
			it.flag |= FLAG_WithBeginInterval
		}

		e.index[id] = it
		heap.Push(&e.heap, it)
		DebugStepf("%v insert, flag=%d dna %d, next %v", id, it.flag, it.dna, time.Unix(0, it.nextTime).Format(time.RFC3339Nano))
		e.cond.Signal() // 新增任务后唤醒等待的loop协程
	}
	return nil
}

/* ------------------ 新增/更新任务 ------------------ */
func (e *IntervalListEngine) OpenInterval(id interface{}, interval time.Duration, fn func()) error {
	return e.OpenIntervalWithNextRunAt(id, zeroTime, interval, fn)
}

/* -------------------- 关闭任务 -------------------- */
func (e *IntervalListEngine) CloseInterval(id interface{}) error {
	t0 := time.Now()
	defer func() {
		ms := time.Since(t0)
		if ms > time.Millisecond*100 {
			SlowDebugf("slow closeInterval %v", ms)
		}
	}()
	e.mu.Lock()
	it, ok := e.index[id]
	if !ok {
		e.mu.Unlock()
		return errors.New("not found")
	}
	DebugStepf("%v remove", id)
	it.interval = -1 // 关闭
	it.dna = itemDNA.Add(1)
	delete(e.index, id)
	if it.heapIdx != -1 {
		heap.Remove(&e.heap, it.heapIdx) // O(log n)
	}
	e.mu.Unlock() // 释放锁，让任务可以完成

	go func() {
		if GoFunCatchException {
			defer DeferCatchPanic()
		}

		// 等待任务执行完成后进行清理
		it.wg.Wait()
		it.fn = nil
		it.id = nil
		DebugStepf("%v cleanup", id)
	}()

	return nil
}

/* -------------------- 停止引擎 -------------------- */
func (e *IntervalListEngine) Stop() {
	e.cancel()
	e.mu.Lock()
	// 清理所有资源
	for id := range e.index {
		delete(e.index, id)
	}
	e.heap = nil
	e.cond.Signal() // 唤醒可能在等待的loop协程，确保能退出
	e.mu.Unlock()
}

/* ===================== 调度主循环 ===================== */
func (e *IntervalListEngine) loop() {
	if GoFunCatchException {
		defer DeferCatchPanic()
	}

	defer func() {
		Debugf("loop end")
		e.loopStep = fmt.Sprintf("%s loop end", time.Now().Format(time.RFC3339))
	}()
	for {
		e.loopStep = fmt.Sprintf("%s loop lock", time.Now().Format(time.RFC3339))
		e.mu.Lock()
		// 循环检查堆是否为空（处理虚假唤醒）
		for len(e.heap) == 0 {
			e.loopStep = fmt.Sprintf("%s heap zero -1", time.Now().Format(time.RFC3339))
			e.timer = nil
			// 检查是否需要退出
			select {
			case <-e.ctx.Done():
				e.mu.Unlock()
				return
			default:
			}
			e.loopStep = fmt.Sprintf("%s heap zero -2", time.Now().Format(time.RFC3339))
			// 释放锁并等待新任务
			e.cond.Wait()
		}

		top := e.heap[0]
		now := time.Now().UnixNano()
		if now < top.nextTime { // 检测第一个是否需要执行
			wait := time.Duration(top.nextTime - now)
			e.mu.Unlock()
			if wait > maxSleep { // 避免等待时间过程错过关闭请求
				wait = maxSleep
			}
			tm := time.NewTimer(wait)
			e.loopStep = fmt.Sprintf("%s loop wait next %v", time.Now().Format(time.RFC3339), wait.String())
			select {
			case <-tm.C:
			case <-e.ctx.Done():
				tm.Stop()
				return
			}
			tm.Stop()
			continue
		}

		// 弹出堆顶
		top = heap.Pop(&e.heap).(*intvListItem)
		fn := top.fn
		wg := &top.wg
		wg.Add(1) // 等待执行
		dna := top.dna
		e.mu.Unlock() // 执行用户回调前释放锁

		go func(itm *intvListItem, dna int32, wg *sync.WaitGroup, fn func()) {
			if GoFunCatchException {
				defer DeferCatchPanic()
			}
			defer func() {
				e.reschedule(itm, dna)
				wg.Done()
			}()
			if dna != itm.dna { // 已经重新更新
				return
			}
			now = time.Now().UnixNano()
			dv := time.Duration(now - itm.nextTime)
			if dv > time.Millisecond*10 {
				SlowDebugf("%v execute delay %v", itm.id, dv)
			}
			fn()
		}(top, dna, wg, fn)
	}

}

/* ---------------- reschedule：跑完塞回去 ---------------- */
func (e *IntervalListEngine) reschedule(itm *intvListItem, dna int32) {
	t0 := time.Now()
	defer func() {
		ms := time.Since(t0)
		if ms > time.Millisecond {
			SlowDebugf("slow reschedule %v", ms)
		}
	}()
	select {
	case <-e.ctx.Done():
		return
	default:
	}
	e.mu.Lock()
	if itm.interval == -1 { // 已经关闭
		e.mu.Unlock()
		return
	}
	if itm.dna == dna { // 未更新，如果更新不处理nextTime
		if itm.flag&FLAG_WithBeginInterval == FLAG_WithBeginInterval {
			itm.nextTime = NextInterval(time.Now(), itm.beginTime, itm.interval).UnixNano()
		} else {
			itm.nextTime = time.Now().UnixNano() + int64(itm.interval)
		}

	}

	heap.Push(&e.heap, itm)
	// e.push(itm)
	e.cond.Signal() // 新增任务后唤醒等待的loop协程
	e.mu.Unlock()
}
